////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Michiel Helvensteijn                                    //
//                                                                            //
// This file is part of the Mist compiler.                                    //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// along with the Mist compiler.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////////

#ifndef COMMON_HPP
#define COMMON_HPP

/******************************************************************************
 * Includes                                                                   *
 ******************************************************************************/

#include <string>
using std::string;

#include <sstream>
using std::ostringstream;
using std::istringstream;

#include <vector>
using std::vector;

#include <boost/shared_ptr.hpp>
using boost::shared_ptr;

#include "assert.hpp"

/******************************************************************************
 * Function Declarations                                                      *
 ******************************************************************************/

/**
 * Convert a value of type \a T to type \e string.
 * 
 * @param val The value to convert to a string.
 * @return The string representation of \a val.
 */
template <class T>
string to_string(T val);

template <class T>
bool is_prefix(const vector<T>& part, const vector<T>& whole);

template <class T>
T max(T a, T b);

template <class T>
unsigned short digit_count(T value);

/**
 * Convert a \e string value to type \a T.
 * 
 * @tparam[in] Type The type to convert \a str to.
 * @param[in] str The string to convert.
 * @return The value converted from \a str.
 */
template<class T>
T to(const string& str);

/**
 * Check if a \e string can be converted to type \a T.
 * 
 * @tparam[in] Type The type for which to check \a str.
 * @param[in] str The string to check the value of.
 * @return Whether \a str can be converted to type \a T.
 */
template<class T>
bool is(const string& str);

template<class T>
string implode(const vector<T>& list, string separator);

template<class T>
string implode(const vector<T>& list, string separator, string last);

/**
 * Get the ordinal indicator belonging to \a number in the English language.
 * 
 * @param number The number to get the ordinal indicator for.
 * @return The ordinal indicator for \a number. One of "st", "nd", "rd", "th".
 */
string ordinal_indicator(unsigned int number);

/**
 * Get the ordinal notation for \a number.
 * 
 * @param number The number to get the ordinal notation for.
 * @return The ordinal notation for \a number. One of
 *         "#st", "#nd", "#rd", "#th", where '#' is \a number.
 */
string ordinal(unsigned int number);

/******************************************************************************
 * Convenience Macro's                                                        *
 ******************************************************************************/

#define DEFINE_POINTER(PropertyType, PropertyName)                     \
	public:                                                             \
		                                                                 \
		void set_##PropertyName (shared_ptr<PropertyType> PropertyName) { \
			assert(PropertyName);                                         \
			_##PropertyName = PropertyName;                               \
		}                                                                 \
		                                                                  \
		shared_ptr<PropertyType> PropertyName() const {                   \
			assert(_##PropertyName);                                      \
			return _##PropertyName;                                       \
		}                                                                 \
	                                                                      \
	private:                                                              \
		                                                                  \
		shared_ptr<PropertyType> _##PropertyName;                         \
	                                                                      \
	public:

#define DEFINE_VALUE(PropertyType, PropertyName)                  \
	public:                                                        \
		                                                            \
		void set_##PropertyName (const PropertyType& PropertyName) { \
			_##PropertyName = PropertyName;                          \
		}                                                            \
		                                                             \
		const PropertyType& PropertyName() const {                   \
			return _##PropertyName;                                  \
		}                                                            \
	                                                                 \
	private:                                                         \
		                                                             \
		PropertyType _##PropertyName;                                \
	                                                                 \
	public:

#define DEFINE_BOOLEAN(Verb, PropertyName)         \
	public:                                         \
		                                             \
		void set_##PropertyName (bool PropertyName) { \
			_##PropertyName = PropertyName;           \
		}                                             \
		                                              \
		bool Verb##_##PropertyName() const {          \
			return _##PropertyName;                   \
		}                                             \
	                                                  \
	private:                                          \
		                                              \
		bool _##PropertyName;                         \
	                                                  \
	public:

#define DEFINE_OPTIONAL_POINTER(PropertyType, PropertyName)            \
	public:                                                             \
		                                                                 \
		void set_##PropertyName (shared_ptr<PropertyType> PropertyName) { \
			assert(PropertyName);                                         \
			_##PropertyName = PropertyName;                               \
		}                                                                 \
		                                                                  \
		shared_ptr<PropertyType> PropertyName() const {                   \
			assert(_##PropertyName);                                      \
			return _##PropertyName;                                       \
		}                                                                 \
		                                                                  \
		bool PropertyName##_is_set() const {                              \
			return (_##PropertyName);                                     \
		}                                                                 \
		                                                                  \
		void delete_##PropertyName() {                                    \
			_##PropertyName.reset();                                      \
		}                                                                 \
	                                                                      \
	private:                                                              \
		                                                                  \
		shared_ptr<PropertyType> _##PropertyName;                         \
	                                                                      \
	public:


#define DEFINE_OPTIONAL_VALUE(PropertyType, PropertyName)         \
	public:                                                        \
		                                                            \
		void set_##PropertyName (const PropertyType& PropertyName) { \
			_##PropertyName.reset(new PropertyType(PropertyName));   \
		}                                                            \
		                                                             \
		const PropertyType& PropertyName() const {                   \
			assert(_##PropertyName);                                 \
			return *_##PropertyName;                                 \
		}                                                            \
		                                                             \
		bool PropertyName##_is_set() const {                         \
			return (_##PropertyName);                                \
		}                                                            \
		                                                             \
		void delete_##PropertyName() {                               \
			_##PropertyName.reset();                                 \
		}                                                            \
	                                                                 \
	private:                                                         \
		                                                             \
		shared_ptr<PropertyType> _##PropertyName;                    \
	                                                                 \
	public:


#define DEFINE_POINTER_LIST(ElementType, ElementName, PluralSuffix)                               \
	public:                                                                                        \
		                                                                                            \
		const vector<shared_ptr<ElementType> >& ElementName##PluralSuffix() const {                  \
			return _##ElementName##PluralSuffix;                                                      \
		}                                                                                              \
		                                                                                                \
		unsigned int ElementName##_count() const {                                                       \
			return _##ElementName##PluralSuffix.size();                                                   \
		}                                                                                                  \
		                                                                                                    \
		void remove_##ElementName(unsigned int index) {                                                     \
			assert(0 <= index && index < ElementName##_count());                                            \
			                                                                                                \
			_##ElementName##PluralSuffix.erase(_##ElementName##PluralSuffix.begin() + index);               \
		}                                                                                                   \
		                                                                                                    \
		void insert_##ElementName(shared_ptr<ElementType> ElementName, unsigned int index) {                \
			assert(0 <= index && index <= ElementName##_count() && ElementName##_count() < UINT_MAX);       \
			assert(ElementName);                                                                            \
			                                                                                                \
			_##ElementName##PluralSuffix.insert(_##ElementName##PluralSuffix.begin() + index, ElementName); \
		}                                                                                                   \
		                                                                                                    \
		void insert_##ElementName(shared_ptr<ElementType> ElementName) {                                    \
			assert(ElementName##_count() < UINT_MAX);                                                       \
			assert(ElementName);                                                                            \
			                                                                                                \
			_##ElementName##PluralSuffix.push_back(ElementName);                                            \
		}                                                                                                   \
	                                                                                                        \
	private:                                                                                                \
		                                                                                                    \
		vector<shared_ptr<ElementType> > _##ElementName##PluralSuffix;                                      \
	                                                                                                        \
	public:


#define DEFINE_VALUE_LIST(ElementType, ElementName, PluralSuffix)                                 \
	public:                                                                                        \
		                                                                                            \
		const vector<ElementType>& ElementName##PluralSuffix() const {                               \
			return _##ElementName##PluralSuffix;                                                      \
		}                                                                                              \
		                                                                                                \
		unsigned int ElementName##_count() const {                                                       \
			return _##ElementName##PluralSuffix.size();                                                   \
		}                                                                                                  \
		                                                                                                    \
		void remove_##ElementName(unsigned int index) {                                                     \
			assert(0 <= index && index < ElementName##_count());                                            \
			                                                                                                \
			_##ElementName##PluralSuffix.erase(_##ElementName##PluralSuffix.begin() + index);               \
		}                                                                                                   \
		                                                                                                    \
		void insert_##ElementName(ElementType ElementName, unsigned int index) {                            \
			assert(0 <= index && index <= ElementName##_count() && ElementName##_count() < UINT_MAX);       \
			                                                                                                \
			_##ElementName##PluralSuffix.insert(_##ElementName##PluralSuffix.begin() + index, ElementName); \
		}                                                                                                   \
		                                                                                                    \
		void insert_##ElementName(ElementType ElementName) {                                                \
			assert(ElementName##_count() < UINT_MAX);                                                       \
			                                                                                                \
			_##ElementName##PluralSuffix.push_back(ElementName);                                            \
		}                                                                                                   \
	                                                                                                        \
	private:                                                                                                \
		                                                                                                    \
		vector<ElementType> _##ElementName##PluralSuffix;                                                   \
	                                                                                                        \
	public:


/******************************************************************************
 * Template Function Implementations                                          *
 ******************************************************************************/

template <class T>
string to_string(T val) {
	ostringstream strout;
	strout << val;
	return strout.str();
}

template <class T>
bool is_prefix(const vector<T>& part, const vector<T>& whole) {
	if (whole.size() < part.size())
		return false;
	
	for (unsigned int i = 0; i < part.size(); ++i) {
		if (part[i] != whole[i])
			return false;
	}
	
	return true;
}

template <class T>
T max(T a, T b) {
	return (b > a ? b : a);
}

template <class T>
unsigned short digit_count(T value) {
	assert(value > 0);
	
	unsigned short result = 0;
	
	while (value > 0) {
		++result;
		value /= 10;
	}
	
	return result;
}

template <class T>
T to(const string& str) {
	istringstream strin(str);
	T result;
	strin >> result;
	assert(strin && strin.eof());
	return result;
}

template<class T>
bool is(const string& str) {
	istringstream strin(str);
	T temp;
	strin >> temp;
	return strin && strin.eof();
}

template<class T>
string implode(const vector<T>& list, string separator) {
	string result("");
	
	for (unsigned int i = 0; i < list.size(); ++i) {
		result += to_string(list[i]);
		if (i + 1 < list.size())
			result += separator;
	}
	
	return result;
}

template<class T>
string implode(const vector<T>& list, string separator, string last) {
	string result("");
	
	for (unsigned int i = 0; i < list.size(); ++i) {
		result += to_string(list[i]);
		if (i + 2 < list.size())
			result += separator;
		else if (i + 1 < list.size())
			result += last;
	}
	
	return result;
}

#endif // COMMON_HPP
